import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.codec.binary.Base64;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

import com.sap.aii.mapping.api.AbstractTrace;
import com.sap.aii.mapping.api.StreamTransformation;
import com.sap.aii.mapping.api.StreamTransformationConstants;
import com.sap.aii.mapping.lookup.Channel;
import com.sap.aii.mapping.lookup.LookupService;
import com.sap.aii.mapping.lookup.RfcAccessor;
import com.sap.aii.mapping.lookup.XmlPayload;

public class B2B_CreateMailPackage implements StreamTransformation
{

	private String inxml = new String();

	private Document mailPackage;
	private Element mailRoot;

	private String businessSystem;
	private final String commChannel = "GRC_Receiver_RFC";
	private final String mailBoundary = "--AaZz";

	private String idNFe;
	private String nSeqEvento;
	private String tpAmb;

	private String mailBody;
	private String mailText;
	private String mailTo;
	private String mailFrom;
	private String mailSubject;
	private String attachmentFilenameXML;
	private String attachmentFilenamePDF;
	private String danfeBinary;

	private String newLine = System.getProperty("line.separator");

	AbstractTrace trace;
	private Map param = null;

	public void execute(InputStream in, OutputStream out)
	{

		trace = (AbstractTrace) param.get(StreamTransformationConstants.MAPPING_TRACE);

		StringBuffer strbuffer;

		// Carrega o stream para parse do XML
		try
		{
			strbuffer = new StringBuffer();
			byte[] b = new byte[4096];
			for (int n; (n = in.read(b)) != -1;)
			{
				strbuffer.append(new String(b, 0, n));
			}
			inxml = strbuffer.toString();
		}
		catch (Exception e)
		{
			// trace.addWarning(e.getMessage());
		}

		// Carrega documento recebido na variavel 'inxml'
		GetStringFromStream();

		// Busca os dados de remetente e destinatario no GRC;
		CustomRFCLookup();

		// Cria arvore 'mailPackage' para append dos nodos internos
		ParseDocument();

		attachmentFilenameXML = idNFe + ".xml";
		attachmentFilenamePDF = idNFe + ".pdf";

		// Apos popular variaveis de conteudo, cria o mailBody
		mailBody = newLine + "--" + mailBoundary + newLine + CreateBodyText() + newLine + newLine + "--" + mailBoundary + newLine
				+ CreateBodyAttachmentXML();

		if (danfeBinary.length() > 2)
		{
			mailBody = mailBody + newLine + "--" + mailBoundary + newLine + CreateBodyAttachmentPDF() + newLine;
		}
		// Nodo Subject
		CreateNode("Subject", mailSubject);

		// Nodo From
		CreateNode("From", mailFrom);

		// Nodo To
		CreateNode("To", mailTo);

		// Nodo Content-Type
		CreateNode("Content_Type", "multipart/mixed; boundary=\"" + mailBoundary + "\"");

		// Nodo Content
		CreateNode("Content", mailBody);

		try
		{
			TransformerFactory tf = TransformerFactory.newInstance();
			Transformer transformer = tf.newTransformer();
			transformer.setOutputProperty(OutputKeys.METHOD, "xml");
			transformer.transform(new DOMSource(mailPackage), new StreamResult(out));
		}
		catch (Exception e)
		{
			trace.addWarning(e.getMessage());
		}
	}

	public void setParameter(Map param)
	{
		this.param = param;
		if (param == null)
		{
			this.param = new HashMap();
		}
	}

	private void CreateNode(String nodeName, String nodeValue)
	{
		Element node;
		Text nodeContent;
		try
		{
			node = mailPackage.createElement(nodeName);
			mailRoot.appendChild(node);
			nodeContent = mailPackage.createTextNode(nodeValue);
			node.appendChild(nodeContent);
		}
		catch (Exception e)
		{
			trace.addWarning(nodeName + " " + nodeValue + " " + e.getMessage());
		}
	}

	private void ParseDocument()
	{
		DocumentBuilderFactory docBuilderFactory;
		DocumentBuilder docBuilder;

		try
		{
			docBuilderFactory = DocumentBuilderFactory.newInstance();
			docBuilder = docBuilderFactory.newDocumentBuilder();
			// Cria escopo do XML
			mailPackage = docBuilder.newDocument();
			mailRoot = mailPackage.createElement("ns1:Mail");
			mailRoot.setAttribute("xmlns:ns1", "http://sap.com/xi/XI/Mail/30");
			mailPackage.appendChild(mailRoot);
		}
		catch (Exception e)
		{
			trace.addWarning("ParseDocument" + e.getMessage());
		}
	}

	private void GetStringFromStream()
	{
		// Busca a tag da Chave NF-e a partir da String inxml
		// Busca tambem o tpAmb para definir business system
		DocumentBuilder docBuilder;
		Document doc = null;

		try
		{
			docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			doc = docBuilder.parse(inxml);
		}
		catch (Exception e)
		{
			trace.addWarning("GetStringFromStream: Error on parsing document: " + e.getMessage());
		}

		if (doc != null)
		{
			try
			{
				idNFe = doc.getElementsByTagName("chNFe").item(0).getFirstChild().getNodeValue();
				tpAmb = doc.getElementsByTagName("tpAmb").item(0).getFirstChild().getNodeValue();
				if (tpAmb.equals("1"))
				{
					businessSystem = "APPLICATION_SERVER_NFP";
				}
				else
				{
					businessSystem = "APPLICATION_SERVER_NFD";
				}
			}
			catch (Exception e)
			{
				trace.addWarning("GetStringFromStream: Error on retrieving tags: " + e.getMessage());
			}

			// Procura a tag nSeqEvento, para indicar a ordem de sequencia
			// caso documento seja um evento emitido.
			// Inicializa a variavel com 0 caso seja emissao
			try
			{
				nSeqEvento = doc.getElementsByTagName("nSeqEvento").item(0).getFirstChild().getNodeValue();
			}
			catch (Exception e)
			{
				trace.addInfo("GetStringFromStream: " + "Tag '<nSeqEvento>' nao encontrada.\n" + e.getMessage());
				nSeqEvento = new String("0");
			}
		}
	}

	private String CreateBodyText()
	{
		String contentType = "Content-Type: text/html; charset=utf-8";
		String contentDisposition = "Content-Disposition: inline";

		return contentType + newLine + contentDisposition + newLine + newLine + mailText;
	}

	private String CreateBodyAttachmentXML()
	{
		byte[] encodedBytes = Base64.encodeBase64(inxml.getBytes());
		String escapeLineLength = new String(encodedBytes);
		String contentType = "Content-Type: application/text; name=" + attachmentFilenameXML;
		String contentTransferEncoding = "Content-Transfer-Encoding: base64";
		String contentDisposition = "Content-Disposition: attachment; filename=" + attachmentFilenameXML;
		return contentType + newLine + contentTransferEncoding + newLine + contentDisposition + newLine + newLine + escapeLineLength;
	}

	private String CreateBodyAttachmentPDF()
	{
		String contentTypePDF = "Content-Type: application/pdf; name=" + attachmentFilenamePDF;
		String contentDispositionPDF = "Content-Disposition: attachment; filename=" + attachmentFilenamePDF;
		return contentTypePDF + newLine + contentDispositionPDF + newLine + newLine + danfeBinary;
	}

	/**
	 * 
	 */
	private void CustomRFCLookup()
	{
		// Busca dados do GRC a partir da chave NF-e;

		RfcAccessor rfcCaller;
		Channel rfcChannel;
		XmlPayload request;
		XmlPayload response;
		InputStream inputRFC;
		DocumentBuilder docBuilder;
		Document doc;

		String req = "<ns0:ZNFE_GET_OB2B xmlns:" + "ns0='urn:sap-com:document:sap:rfc:functions'>" + "<IV_CHNFE>" + idNFe + "</IV_CHNFE>"
				+ "<IV_NSEQEVENTO>" + nSeqEvento + "</IV_NSEQEVENTO>" + "</ns0:ZNFE_GET_OB2B>";

		trace.addWarning(req);

		try
		{
			// Cria informacoes sobre conexao RFC com ABAP
			rfcChannel = LookupService.getChannel(this.businessSystem, this.commChannel);
			rfcCaller = LookupService.getRfcAccessor(rfcChannel);

			// Efetua comunicacao com a funcao RFC no ABAP
			inputRFC = new ByteArrayInputStream(req.getBytes("UTF-8"));
			request = LookupService.getXmlPayload(inputRFC);
			response = rfcCaller.call(request);
			inputRFC = response.getContent();

			// A partir do XML de resposta, busca os campos de dados
			docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			doc = docBuilder.parse(inputRFC);

			try
			{
				mailTo = doc.getElementsByTagName("EV_TARGET_EMAIL").item(0).getFirstChild().getNodeValue();
				mailFrom = doc.getElementsByTagName("EV_SOURCE_EMAIL").item(0).getFirstChild().getNodeValue();
				mailSubject = doc.getElementsByTagName("EV_MAIL_SUBJECT").item(0).getFirstChild().getNodeValue();
				mailText = doc.getElementsByTagName("EV_MAIL_TEXT").item(0).getFirstChild().getNodeValue();
				danfeBinary = doc.getElementsByTagName("EV_DANFE_BINARY").item(0).getFirstChild().getNodeValue();

			}
			catch (Exception e)
			{
				trace.addWarning("CustomRFCLookup: Incomplete data received from RFC lookup." + "\nReview function call" + "\nException: "
						+ e.getMessage() + "\nRFC Call response: " + inputRFC.toString());
			}
		}
		catch (Exception e)
		{
			trace.addWarning("CustomRFCLookup " + e.getMessage());
		}
	}
}
